package com.xy.netty.dome.start;

import com.xy.netty.dome.handler.EchoServerDelimiterHandler;
import com.xy.netty.dome.handler.EchoServiceLengthFieldBasedHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.ByteOrder;

/**
 * @author niuniu
 */
@Slf4j
public class EchoLengthFieldBasedServer {

    private final int port;

    public EchoLengthFieldBasedServer(int port){
        this.port = port;
    }

    /**
     * 启动流程
     */
    public void run() throws InterruptedException {
        //配置服务端线程组
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            /**
                             *   基于长度解码的通用解码器
                             *      maxFrameLength：
                             *                      数据包的最大长度
                             *     	lengthFieldOffset：
                             *     	                    长度域偏移量，指的是长度域位于整个数据包字节数组中的下标；
                             *     	                长度字段的偏移位，长度字段开始的地方，意思是跳过指定长度个字节之后的才是消息体字段
                             *     	lengthFieldLength：
                             *     	                    长度域的自己的字节数长度。
                             *     	                长度字段占的字节数, 帧数据长度的字段本身的长度
                             *     	lengthAdjustment：
                             *     	                     长度域的偏移量矫正。
                             *     	                     如果长度域的值，除了包含有效数据域的长度外，还包含了其他域（如长度域自身）长度，
                             *     	                     那么，就需要进行矫正。矫正的值为：包长 - 长度域的值 – 长度域偏移 – 长度域长。
                             *     	                一般 Header + Body，添加到长度字段的补偿值,如果为负数，
                             *     	                开发人员认为这个 Header的长度字段是整个消息包的长度，则Netty应该减去对应的数字
                             *     	initialBytesToStrip：
                             *     	                     丢弃的起始字节数。
                             *     	                     丢弃处于有效数据前面的字节数量。
                             *     	                     比如前面有4个节点的长度域，则它的值为4。
                             *     	                从解码帧中第一次去除的字节数, 获取完一个完整的数据包之后，
                             *     	                忽略前面的指定位数的长度字节，应用解码器拿到的就是不带长度域的数据包
                             *     	failFast：
                             *     	                是否快速失败
                             */
                            /**
                             *  说明：偏移量0处的2字节长度字段，不带标题
                             *  内容说明：
                             *  BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
                             *  +--------+----------------+      +--------+----------------+
                             *  | Length | Actual Content |----->| Length | Actual Content |
                             *  | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
                             *  +--------+----------------+      +--------+----------------+
                             */
                            //案例序列：1
//                            ch.pipeline().addLast(
//                                    new LengthFieldBasedFrameDecoder(
//                                            ByteOrder.BIG_ENDIAN,
//                                            Integer.MAX_VALUE,
//                                    0,
//                                    2,
//                                    0,
//                                    0, //(= do not strip header)
//                                            false
//                                    )
//                            );
                            /**
                             *  说明：3字节长度字段位于5字节头的末尾，不带头
                             *  内容说明：
                             *   BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
                             *  +----------+----------+----------------+      +----------+----------+----------------+
                             *  | Header 1 |  Length  | Actual Content |----->| Header 1 |  Length  | Actual Content |
                             *  |  0xCAFE  | 0x00000C | "HELLO, WORLD" |      |  0xCAFE  | 0x00000C | "HELLO, WORLD" |
                             *  +----------+----------+----------------+      +----------+----------+----------------+
                             */
                         ch.pipeline().addLast(
                                    new LengthFieldBasedFrameDecoder(
                                            ByteOrder.BIG_ENDIAN,
                                            Integer.MAX_VALUE,
                                            2,
                                            3,
                                            0,
                                            0, //(= do not strip header)
                                            false
                                    )
                            );
                            /**
                             *  说明：3字节长度字段位于5字节头的开头，不带头
                             *  内容说明：
                             *   BEFORE DECODE (17 bytes)                      AFTER DECODE (17 bytes)
                             *  +----------+----------+----------------+      +----------+----------+----------------+
                             *  |  Length  | Header 1 | Actual Content |----->|  Length  | Header 1 | Actual Content |
                             *  | 0x00000C |  0xCAFE  | "HELLO, WORLD" |      | 0x00000C |  0xCAFE  | "HELLO, WORLD" |
                             *  +----------+----------+----------------+      +----------+----------+----------------+
                             */
//                         ch.pipeline().addLast(
//                                    new LengthFieldBasedFrameDecoder(
//                                    maxFrameLength,
//                                    2,
//                                    3,
//                                    2,//(= the length of Header 1)
//                                    0
//                                    )
//                            );
                            /**
                             *  说明：在4字节头部中间偏移1的2字节长度字段，带头头字段和长度字段
                             *  内容说明：
                             *   BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
                             *  +------+--------+------+----------------+      +------+----------------+
                             *  | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
                             *  | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
                             *  +------+--------+------+----------------+      +------+----------------+
                             */
//                         ch.pipeline().addLast(
//                                    new LengthFieldBasedFrameDecoder(
//                                    maxFrameLength,
//                                    1,// (= the length of HDR1)
//                                    2,
//                                    1,//(= the length of HDR2)
//                                    3// (= the length of HDR1 + LEN)
//                                    )
//                            );
                            /**
                             *  说明：在4字节头部中间偏移1的2字节长度字段，带头头字段和长度字段，长度字段表示整个消息的长度。
                             *  内容说明：
                             *  BEFORE DECODE (16 bytes)                       AFTER DECODE (13 bytes)
                             *  +------+--------+------+----------------+      +------+----------------+
                             *  | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
                             *  | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" |      | 0xFE | "HELLO, WORLD" |
                             *  +------+--------+------+----------------+      +------+----------------+
                             */
//                         ch.pipeline().addLast(
//                                    new LengthFieldBasedFrameDecoder(
//                                    maxFrameLength,
//                                    1,
//                                    2,
//                                    -3,//(= the length of HDR1 + LEN, negative)
//                                    3
//                                    )
//                            );
//                            ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                            ch.pipeline().addLast(new EchoServiceLengthFieldBasedHandler());
                        }
                    });
            //绑定端口，同步等待成功
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
            if(channelFuture.isSuccess()){
                log.info("服务端启动端口：{}",port);
            }
            //等待服务端监听端口关闭
            channelFuture.channel().closeFuture().sync();
        }finally {

            //优雅退出，释放线程池
            workGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }



    }


    public static void main(String [] args) throws InterruptedException {
        int port = 8080;
        if(args.length > 0){
            port = Integer.parseInt(args[0]);
        }

        new EchoLengthFieldBasedServer(port).run();

    }

}
